﻿using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Display;
using Windows.Security.Cryptography;
using Windows.UI;
using Windows.UI.ViewManagement;
using static FASTControlUI.GameDevice;

namespace FASTControlUI
{
    public class ListItemData
    {
        public int Id
        {
            get; set;
        }
        public string Name
        {
            get; set;
        }
    }

    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class WheelPage : Page
    {
        static OutpurReportPedal outputReportPedal = new OutpurReportPedal();
        internal static OutputReportWheel outputReportWheel = new OutputReportWheel();
        InputReport InputReport { get; set; }
        float tY = 0;
        private TypedEventHandler<GameDevice, GameReportArgs> handler;
        private void ReciveHandle(GameDevice device, GameReportArgs args)
        {
            InputReport = args.Report;
        }
        public WheelPage()
        {
            this.InitializeComponent();
            forceStren.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            stopPosition.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            damping.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            centerForce.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            antigravity.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            maxAngle.AddHandler(PointerReleasedEvent, new PointerEventHandler(Slider_PointerReleased), true);
            PedalStructInit(ref outputReportPedal);
            handler = ReciveHandle;
            HomePage.gameInputDevice.InputReportHandleEvent += handler;
            styleColor = MainConfig.Current.LineColor;
            wave.Add(new Vector2(0, (float)(virtualCanvas.Height / 2)));
            outputReportWheel = GameDataPublic.ReportWheel;
            outputReportPedal = GameDataPublic.ReportPedal;
            outputReportPedal.id = 0x0d;
            outputReportWheel.id = 0x0e;
            forceStren.Value = outputReportWheel.Force * 100 / 255;
            stopPosition.Value = outputReportWheel.WheelStop * 100 / 255;
            damping.Value = outputReportWheel.Friction * 100 / 255;
            centerForce.Value = outputReportWheel.WheelCenter * 100 / 255;
            antigravity.Value = outputReportWheel.Antigravity * 100 / 255;
            maxAngle.Value = outputReportWheel.WheelDegrees;
            int index = 3;
            UpdatePedal(index--);
            UpdatePedal(index--);
            UpdatePedal(index--);
            UpdatePedal(index--);
            collection = new ObservableCollection<ListItemData>(DataAccess.GetData());
            dataGrid.ItemsSource = collection;
        }
        ~WheelPage()
        {
            HomePage.gameInputDevice.InputReportHandleEvent -= handler;
        }
        private void PedalStructInit(ref OutpurReportPedal report)
        {

            report.id = 0x0d;
            report.ThrottleLow = 0;
            report.ThrottleHigh = 0;
            report.BrakeLow = 0;
            report.BrakeHigh = 0;
            report.RxLow = 0;
            report.RxHigh = 0;
            report.RyLow = 0;
            report.RyHigh = 0;
            report.BrakeBezier = 0;
            report.ThrottleBezier = 0;
            report.RxBezier = 0;
            report.RyBezier = 0;
            report.waveFiltering = 0;
        }
        private void Button_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
        {
            Button button = sender as Button;
            outputReportWheel.WheelFlag = 0;
            if ((string)button.Tag == "Center")
            {
                outputReportWheel.WheelFlag = 0x44;
            }
            if ((string)button.Tag == "Test")
            {
                outputReportWheel.WheelFlag = 0x01;
                short angle = 0;
                if (outputReportWheel.WheelEngle != angle)
                    outputReportWheel.WheelEngle = 0;
                else
                    outputReportWheel.WheelEngle = (short)(angle - 30);
            }
            _ = HomePage.gameOutputDevice.Write(outputReportWheel);
            GameDataPublic.ReportWheel = outputReportWheel;
        }
        private void Slider_PointerReleased(Object sender, PointerRoutedEventArgs e)
        {
            Slider slider = sender as Slider;
            int tag = int.Parse((string)slider.Tag);
            byte value = (byte)(slider.Value / 100 * 255);
            switch (tag)
            {
                case 0:
                    outputReportWheel.Force = value;
                    break;
                case 1:
                    outputReportWheel.WheelStop = value;
                    break;
                case 2:
                    outputReportWheel.Friction = value;
                    break;
                case 3:
                    outputReportWheel.WheelCenter = value;
                    break;
                case 4:
                    outputReportWheel.Antigravity = value;
                    break;
                case 5:
                    outputReportWheel.WheelDegrees = (short)slider.Value;
                    break;
                default:
                    break;
            }
        }
        Color styleColor;
        static Vector3[] Axis = new Vector3[4];
        int AxisSelect = 0;
        private void CanvasControl_Draw(CanvasControl sender, CanvasDrawEventArgs args)
        {
            int number = int.Parse((string)sender.Tag);
            Line_Draw(sender, args, Axis[number]);
        }

        private void Line_Draw(in CanvasControl canvas, in CanvasDrawEventArgs args, Vector3 point)
        {
            float min_height = (float)(point.X / 100 * (canvas.ActualHeight - 43)), max_height = (float)(point.Y / 100 * (canvas.ActualHeight - 43)), lineControl = point.Z;
            var canvasCommandList = new CanvasCommandList(canvas);
            using (CanvasDrawingSession clds = canvasCommandList.CreateDrawingSession())
            {
                var width = (float)canvas.ActualWidth - 20;
                var height = (float)(canvas.ActualHeight) - 40;
                using (var cpb = new CanvasPathBuilder(clds))
                {
                    // Horizontal line
                    // 水平线
                    cpb.BeginFigure(new Vector2(3, height));
                    cpb.AddLine(new Vector2(width, height));
                    cpb.EndFigure(CanvasFigureLoop.Open);

                    // Horizontal line arrow
                    // 水平箭头
                    cpb.BeginFigure(new Vector2(width - 10, height - 3));
                    cpb.AddLine(new Vector2(width, height));
                    cpb.AddLine(new Vector2(width - 10, height + 3));
                    cpb.EndFigure(CanvasFigureLoop.Open);
                    clds.DrawGeometry(CanvasGeometry.CreatePath(cpb), styleColor, strokeWidth: 1);
                }
                clds.DrawText("100", new Vector2(width - 30, height - 30), styleColor);
                clds.DrawText("100", new Vector2(10, 0), styleColor);
                int number = int.Parse((string)canvas.Tag);
                switch (number)
                {
                    case 0:
                        tY = InputReport.Throttle / 65535.0f * height;
                        break;
                    case 1:
                        tY = InputReport.Brake / 65535.0f * height;
                        break;
                    case 2:
                        tY = InputReport.axisRx / 65535.0f * height;
                        break;
                    case 3:
                        tY = InputReport.axisRy / 65535.0f * height;
                        break;
                    default: break;
                }
                tY = height - tY;
                using (var cpb = new CanvasPathBuilder(clds))
                {
                    // Vertical line
                    // 垂直线
                    cpb.BeginFigure(new Vector2(3, 0));
                    cpb.AddLine(new Vector2(3, height));
                    cpb.EndFigure(CanvasFigureLoop.Open);

                    // Vertical line arrow
                    cpb.BeginFigure(new Vector2(3 - 3, 10));
                    cpb.AddLine(new Vector2(3, 0));
                    cpb.AddLine(new Vector2(3 + 3, 10));
                    cpb.EndFigure(CanvasFigureLoop.Open);
                    clds.DrawGeometry(CanvasGeometry.CreatePath(cpb), styleColor, strokeWidth: 1);
                }
                Vector2 startPos = new Vector2(3, height - min_height);
                Vector2 controlPos = new Vector2(width / 2 * lineControl, height / 2 * lineControl);
                Vector2 endPos = new Vector2(width, 3 + max_height);
                float a = startPos.Y; float b = controlPos.Y; float c = endPos.Y;
                float ta = (a - 2 * b + c);
                float tb = (2 * b - 2 * a);
                if (tY < 3 + max_height)
                    tY = 3 + max_height;
                if (tY > height - min_height)
                    tY = height - min_height;
                float tc = (a - tY);
                float p = tb * tb - 4 * ta * tc;
                float t = 0;
                if (p >= 0)
                {
                    t = (-tb - MathF.Sqrt(p)) / (2 * ta);
                    if (t < 0 || t > 1.01f)
                    {
                        t = (-tb + MathF.Sqrt(p)) / (2 * ta);
                    }
                }
                float t1 = 1 - t;
                float ay = startPos.X; float by = controlPos.X; float cy = endPos.X;
                float X = ((t1 * t1) * ay) + (2 * t * t1 * by) + t * t * cy;
                Vector2 pos = new Vector2(X, tY);
                clds.FillCircle(pos, 5, Colors.Red);
                Vector2 offset = new Vector2(20, 20);
                Vector2 controlCircle = ((startPos + endPos) / 2 + controlPos) / 2;
                clds.FillCircle(startPos, 3, styleColor);
                clds.FillCircle(endPos, 3, styleColor);
                clds.FillCircle(controlCircle, 3, styleColor);
                using (var cpb = new CanvasPathBuilder(clds))
                {
                    cpb.BeginFigure(startPos);
                    cpb.AddQuadraticBezier(controlPos, endPos);
                    cpb.EndFigure(CanvasFigureLoop.Open);
                    clds.DrawGeometry(CanvasGeometry.CreatePath(cpb), styleColor, strokeWidth: 1);
                }
                // Random random = new Random();
                args.DrawingSession.DrawImage(canvasCommandList);
                canvas.Invalidate();
            }
        }

        private void CanvasVirtualControl_RegionsInvalidated(CanvasVirtualControl sender, CanvasRegionsInvalidatedEventArgs args)
        {
            foreach (var region in args.InvalidatedRegions)
            {
                using (var ds = sender.CreateDrawingSession(region))
                {
                    CanvasStrokeStyle strokeStyle = new CanvasStrokeStyle()
                    {
                        StartCap = CanvasCapStyle.Round,
                        EndCap = CanvasCapStyle.Triangle,
                    };
                    using (var builder = new CanvasPathBuilder(sender))
                    {
                        int i = (int)sender.Width - wave.Count;
                        if (region.X + region.Width < i)
                            break;
                        if (i < region.X)
                            i = (int)region.X;
                        int end = i + wave.Count;
                        if (wave.Count > region.Width)
                            end = i + (int)region.Width;
                        bezierStartPoint.X = i - 1;
                        builder.BeginFigure(bezierStartPoint);
                        int index = wave.Count - 5000 + i;
                        if (index < 0)
                            break;
                        for (int j = i; j < end; j++)
                        {
                            var endPoint = wave[index];
                            index++;
                            endPoint.X = i;
                            i++;
                            //var bezierControl = bezierStartPoint + (endPoint - bezierStartPoint) / 2;
                            //builder.AddQuadraticBezier(bezierControl, endPoint);
                            builder.AddLine(endPoint);
                            bezierStartPoint = endPoint;
                        }
                        builder.EndFigure(CanvasFigureLoop.Open);
                        using (var geometry = CanvasGeometry.CreatePath(builder))
                        {
                            ds.DrawGeometry(geometry, styleColor, 3, strokeStyle);
                        }
                    }
                }
            }
        }

        private static Color GradientColor(float mu)
        {
            byte c = (byte)((Math.Sin(mu * Math.PI * 2) + 1) * 127.5);

            return Color.FromArgb(255, (byte)(255 - c), c, 220);
        }
        Vector2 bezierStartPoint = new Vector2(0, 0);
        List<Vector2> wave = new List<Vector2>();
        private void CanvasVirtualControl_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
        {
            PointerPoint point = e.GetCurrentPoint(virtualCanvas);
            if (point.Properties.IsLeftButtonPressed)
            {
                if (wave.Count > virtualCanvas.Width * 10)
                    wave.Clear();
                wave.Add(new Vector2((float)(point.Position.X), (float)(point.Position.Y)));
                virtualCanvas.Invalidate();
            }
        }
        private void UpdatePedal(int index)
        {
            if (MinValueSlider == null)
                return;
            switch (index)
            {
                case 0:
                    MinValueSlider.Value = outputReportPedal.ThrottleLow;
                    MaxValueSlider.Value = outputReportPedal.ThrottleHigh;
                    ControlValueSlider.Value = outputReportPedal.ThrottleBezier;
                    break;
                case 1:
                    MinValueSlider.Value = outputReportPedal.BrakeLow;
                    MaxValueSlider.Value = outputReportPedal.BrakeHigh;
                    ControlValueSlider.Value = outputReportPedal.BrakeBezier;
                    break;
                case 2:
                    MinValueSlider.Value = outputReportPedal.RxLow;
                    MaxValueSlider.Value = outputReportPedal.RxHigh;
                    ControlValueSlider.Value = outputReportPedal.RxBezier;
                    break;
                case 3:
                    MinValueSlider.Value = outputReportPedal.RyLow;
                    MaxValueSlider.Value = outputReportPedal.RyHigh;
                    ControlValueSlider.Value = outputReportPedal.RyBezier;
                    break;
                default: break;
            }
            Axis[index].X = (float)MinValueSlider.Value;
            Axis[index].Y = (float)MaxValueSlider.Value;
            Axis[index].Z = (float)(ControlValueSlider.Value / 50);
        }
        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ComboBox comboBox = sender as ComboBox;
            AxisSelect = comboBox.SelectedIndex;
            UpdatePedal(AxisSelect);
        }

        private void setMinOutputReportIdx(int idx, byte value)
        {
            switch (idx)
            {
                case 0:
                    outputReportPedal.ThrottleLow = value;
                    break;
                case 1:
                    outputReportPedal.BrakeLow = value;
                    break;
                case 2:
                    outputReportPedal.RxLow = value;
                    break;
                case 3:
                    outputReportPedal.RyLow = value;
                    break;
                default: break;
            }
        }

        private void setMaxOutputReportIdx(int idx, byte value)
        {
            switch (idx)
            {
                case 0:
                    outputReportPedal.ThrottleHigh = value;
                    break;
                case 1:
                    outputReportPedal.BrakeHigh = value;
                    break;
                case 2:
                    outputReportPedal.RxHigh = value;
                    break;
                case 3:
                    outputReportPedal.RyHigh = value;
                    break;
                default: break;
            }
        }

        private void setBezierOutputReportIdx(int idx, byte value)
        {
            switch (idx)
            {
                case 0:
                    outputReportPedal.ThrottleBezier = value;
                    break;
                case 1:
                    outputReportPedal.BrakeBezier = value;
                    break;
                case 2:
                    outputReportPedal.RxBezier = value;
                    break;
                case 3:
                    outputReportPedal.RyBezier = value;
                    break;
                default: break;
            }
        }

        private void min_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
        {
            Axis[AxisSelect].X = (float)e.NewValue;
            setMinOutputReportIdx(AxisSelect, (byte)(e.NewValue));
        }

        private void max_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
        {
            Axis[AxisSelect].Y = (float)e.NewValue;
            setMaxOutputReportIdx(AxisSelect, (byte)(e.NewValue));
        }

        private void Button_Click_1(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
        {
            Button button = sender as Button;
            string tag = (string)button.Tag;
            if (tag == "Calibration")
            {

            }
            _ = HomePage.gameOutputDevice.Write(outputReportPedal);
            GameDataPublic.ReportPedal = outputReportPedal;
        }

        private void Slider_ValueChanged_1(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
        {
            outputReportPedal.waveFiltering = (byte)(e.NewValue / 100.0f * 255);
        }

        private void Slider_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
        {
            Axis[AxisSelect].Z = (float)(e.NewValue / 50);
            setBezierOutputReportIdx(AxisSelect, (byte)(e.NewValue));
        }

        private void Page_Unloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
        {
            if (virtualCanvas != null)
            {
                virtualCanvas.RemoveFromVisualTree();
                virtualCanvas = null;
            }
            if (canvas1 != null)
            {
                canvas1.RemoveFromVisualTree();
                canvas1 = null;
            }
            if (canvas2 != null)
            {
                canvas2.RemoveFromVisualTree();
                canvas2 = null;
            }
            if (canvas3 != null)
            {
                canvas3.RemoveFromVisualTree();
                canvas3 = null;
            }
            if (canvas4 != null)
            {
                canvas4.RemoveFromVisualTree();
                canvas4 = null;
            }
        }
        ObservableCollection<ListItemData> collection;
        int N = 0;
        private void ButtonDataUpdate(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
        {
            Button button = sender as Button;
            string flag = button.Tag as string;
            switch (flag)
            {
                case "add":
                    N++;
                    DataAccess.AddData(InputName.Text);
                    collection.Add(DataAccess.GetLastData());
                    break;
                case "delete":
                    DataAccess.DeleteData();
                    if (collection.Count != 0)
                        collection.RemoveAt(collection.Count - 1);
                    break;
                default:
                    break;
            }
            //dataGrid.ItemsSource = new ObservableCollection<ListItemData>(DataAccess.GetData());
        }
    }
}
